// 
//  Copyright © 2009 Jiří Zárevúcky <zarevucky.jiri@gmail.com>
// 
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU Affero General Public License as
//  published by the Free Software Foundation, either version 3 of the
//  License, or (at your option) any later version.
// 
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU Affero General Public License for more details.
// 
//  You should have received a copy of the GNU Affero General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
// 
// 


using System;

using Galaxium.Protocol.Xmpp.Library.Xml;
using Galaxium.Protocol.Xmpp.Library.Utility;

namespace Galaxium.Protocol.Xmpp.Library.Core
{
	public enum PresType {
		Available, Unavailable, Subscribe, Subscribed,
		Unsubscribe, Unsubscribed, Probe, Error
	}
	
	public class PresenceEventArgs: EventArgs
	{
		public Presence Presence { get; private set; }
		public PresenceEventArgs (Presence presence) { Presence = presence; }
	}

	public class Presence: Stanza, IComparable<Presence>
	{
		public Presence ()
			:base ("presence") 
		{
		}
		
		public Presence (PresType type, JabberID recipient)
			:this ()
		{
			Type = type;
			To = recipient;
		}
		
		public Presence (string show, string status, sbyte? priority)
			:this ()
		{
			if (show != null)
				AppendShow (show);
			AppendStatus (status);
			if (priority != 0)
				AppendPriority (priority);
		}

		public Presence (Element elm)
			:base (elm)
		{
			if (Name != "presence")
				throw new ArgumentException ();
		}
		
		public new PresType Type {
			get {
				switch (base.Type) {
				case null:           return PresType.Available;
				case "unavailable":  return PresType.Unavailable;
				case "subscribe":    return PresType.Subscribe;
				case "subscribed":   return PresType.Subscribed;
				case "unsubscribe":  return PresType.Unsubscribe;
				case "unsubscribed": return PresType.Unsubscribed;
				case "probe":        return PresType.Probe;
				case "error":        return PresType.Error;
				default:             throw new Exception("Unknown presence type");
				}
			}
			set {
				base.Type = value == PresType.Available ? null : value.ToString ().ToLower ();
			}
		}

		public string Show {
			get { return GetTextChild ("show", null); }
		}
		
		public void AppendShow (string show)
		{
			if (show == null)
				throw new ArgumentNullException ("show");
			AppendTextChild ("show", null, show);
		}
		
		public string Status {
			get { return GetTextChild ("status", null); }
		}
		
		public void AppendStatus (string status)
		{
			if (!String.IsNullOrEmpty (status))
				AppendTextChild ("status", null, status);
		}

		public sbyte? Priority {
			get {
				var text = GetTextChild ("priority", null);
				return text == null ? (sbyte?) null : SByte.Parse (text); // FIXME
			}
		}
		
		public void AppendPriority (sbyte? priority)
		{
			if (priority.HasValue)
				AppendTextChild ("priority", null, priority.ToString ());
		}
		
		public int CompareTo (Presence other)
		{
			sbyte pri1 = Priority ?? 0;
			sbyte pri2 = other.Priority ?? 0;
			var comp = pri1.CompareTo (pri2);
			if (comp != 0) return comp;
			return GetValue ().CompareTo (other.GetValue ());
		}
		
		private int GetValue ()
		{
			if (this.Type == PresType.Unavailable) return 0;
			if (this.IsError) return 1;
			switch (Show) {
			case "xa": return 2;
			case "dnd": return 3;
			case "away": return 4;
			case null: return 5;
			case "chat": return 6;
			default: return 7;
			}
		}

		public string GetTextRepresentation ()
		{
			return (Type == PresType.Unavailable) ? "offline" :
				(Type == PresType.Error) ? "error" : (Show ?? "online");
		}

		public string GetDescription ()
		{
			return Type == PresType.Error ? GetErrorDescription () : Status;
		}
		
		public string GetErrorDescription ()
		{
			if (!IsError) return null;
			var desc = Error.GetHumanRepresentation ();
			if (!String.IsNullOrEmpty ((Error.Description ?? String.Empty).Trim ()))
				desc += " (" + Error.Description.Trim () + ")";
			return desc;
		}
	}
}
